package com.hoki.zj.hrm.service.impl;

import com.hoki.zj.hrm.asserts.GlobalAssert;
import com.hoki.zj.hrm.dto.SMSCodeValueInRedisDto;
import com.hoki.zj.hrm.dto.SMSDto;
import com.hoki.zj.hrm.exception.GlobalException;
import com.hoki.zj.hrm.properties.ImageCodeProperty;
import com.hoki.zj.hrm.service.IVerifyCodeService;
import com.hoki.zj.hrm.utils.StrUtils;
import com.hoki.zj.hrm.utils.VerifyCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hoki.zj.hrm.constant.MQMessageConst.EXCHANGE_NAME_MQ;

@Service
@Slf4j
public class VerifyCodeServiceImpl implements IVerifyCodeService {

    @Autowired
    public RedisTemplate<Object, Object> redisTemplate;

    /** 注入图片属性 */
    @Autowired
    private ImageCodeProperty imageProperty;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 获取图片验证码
     * @param imageCodeKey imageCodeKey
     */
    @Override
    public String getPictureVerifyCode(String imageCodeKey) {
        // 1.判断验证码的合法性
        GlobalAssert.isNull(imageCodeKey, "无效的验证码key,请刷新页面!");
        // 2.生成一个4位验证码
        String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
        // 3.存入Redis,key=imageCodeKey,设置过期时间
        redisTemplate.opsForValue().set(imageCodeKey, verifyCode, 3, TimeUnit.MINUTES);
        // 4.生成一个图片验证码,返回BASE64 CODING
        try {
            return VerifyCodeUtils.VerifyCode(imageProperty.getWidth(), imageProperty.getHeight(), verifyCode);
        } catch (IOException e) {
            e.printStackTrace();
            throw new GlobalException("图片验证码获取失败!!");
        }
    }

    /**
     * 2.获取手机验证码
     * @param map mobile  imageCode  imageCodeKey codeType
     * @return smsCode
     */
    @Override
    public void getSmsVerifyCode(Map<String, String> map) {
        String codeType = map.get("codeType"); // mapからコードの所属を取得
        String mobile = map.get("mobile"); // mapから携帯番号を取得
        String imageCode = map.get("imageCode"); // mapから画像コードを取得
        String imageCodeKey = map.get("imageCodeKey"); // mapからから画像コードのキーを取得
        paramCheck(mobile, imageCode, imageCodeKey); // パラメータ検証
        // 1.生成一个6位数手机验证码
        String smsCode = StrUtils.getRandomString(6);
        // 组装key
        String key = codeType + ":" + mobile;
        Date date = new Date();
        // 获取Redis中key对应的值
        SMSCodeValueInRedisDto smsCodeAndTimeFromRedis = (SMSCodeValueInRedisDto) redisTemplate.opsForValue().get(key);
        if (smsCodeAndTimeFromRedis != null) { // 未过期
            Long timeInRedis = smsCodeAndTimeFromRedis.getTimeInRedis(); // 获取Redis中保存的时间
            if (date.getTime() - timeInRedis < 60 * 1000) {
                // 1分钟内连续操作,提示错误
                throw new GlobalException("强撸灰飞烟灭,请休息一下");
            } else {
                // 重新赋值smsCode,发送原来的code
                smsCode = smsCodeAndTimeFromRedis.getSmsCodeInRedis();
            }
        }
        // 2.存入Redis验证码,未过期,将redis中保存的验证码重新赋值然后发送,已过期,或者首次注册直接发送新的验证码
//        String value = smsCode + ":" + date.getTime(); // 组装value 方式一
        // 组装value 方式二
        SMSCodeValueInRedisDto smsCodeValueInRedisDto = new SMSCodeValueInRedisDto(smsCode, date.getTime());
        redisTemplate.opsForValue().set(key, smsCodeValueInRedisDto, 5, TimeUnit.MINUTES);
        // 3.调用消息服务,发送短信
        log.info("手机:{}, 验证码: {}", mobile, smsCode);
        sendRigistrySmsCode(mobile, smsCode);
    }

    /** 发送短信 */
    private void sendRigistrySmsCode(String mobile, String smsCode) {
        SMSDto smsDto = new SMSDto();
        smsDto.setPhoneNum(mobile);
        smsDto.setContent("欢迎您注册,您的验证码为:" + "\r\n"
                + smsCode + "\r\n"
                + "请在5分钟内使用");
        rabbitTemplate.convertAndSend(EXCHANGE_NAME_MQ, "registry.sms", smsDto);
    }

    /** パラメータ検証 */
    private void paramCheck(String mobile, String imageCode, String imageCodeKey) {
        // 1.mobileの有無を判断
        GlobalAssert.isValidPhoneNumber(mobile, "请输入手机号!");
        // 2.imageCodeKeyの有無を判断
        GlobalAssert.hasText(imageCodeKey, "无效的验证码key,请刷新页面!");
        // 3.imageCodeの有無を判断
        GlobalAssert.hasText(imageCodeKey, "图片验证码不能为空!");
        // 4.imageCodeKeyでRedisに保存された元imageCodeを取得
        String originalImageCodeKey = (String) redisTemplate.opsForValue().get(imageCodeKey);
        // 5.originalImageCodeKeyの有無を判断
        GlobalAssert.hasText(originalImageCodeKey, "图片验证码无效,请刷新页面!");
        // 6.mapのimageCodeと比較する
        GlobalAssert.isEquals(imageCode, originalImageCodeKey, "图片验证码错误,请重新输入!");
    }
}
